#include "vcl_stl.h"

#include "bkbase.h"
#include "h_base.h"

#include "h_lz77.h"

#ifdef BK_LINUX
#include <ifaddrs.h>
#endif

#include <sys/socket.h>
#include <netdb.h>

namespace h_base
{

#ifdef H_STRING

HString operator+(const HString& lhs, const HString& rhs)
{
  return HString(lhs) += rhs;
}

bool operator==(const HString& lhs, const HString& rhs)
{
  if ( lhs.length() != rhs.length() )
    return false;

  return strcmp(lhs.c_str(), rhs.c_str()) == 0;
}

bool operator!=(const HString& lhs, const HString& rhs)
{
  if ( lhs.length() != rhs.length() )
    return true;

  return strcmp(lhs.c_str(), rhs.c_str()) != 0;
}

bool operator>(const HString& lhs, const HString& rhs)
{
  return strcmp(lhs.c_str(), rhs.c_str()) > 0;
}

bool operator>=(const HString& lhs, const HString& rhs)
{
  return strcmp(lhs.c_str(), rhs.c_str()) >= 0;
}

bool operator<(const HString& lhs, const HString& rhs)
{
  return strcmp(lhs.c_str(), rhs.c_str()) < 0;
}

bool operator<=(const HString& lhs, const HString& rhs)
{
  return strcmp(lhs.c_str(), rhs.c_str()) <= 0;
}

#endif

HString EnvEvaluate(const HString &Str, map<HString, HString> *EnvMap)
{
   int i, j;
   const char *Env;
   HString Result;
   HString EnvName;

   for ( i = 0; ; )
   {
      j = Str.find('$', i);
      if ( j == HString::npos )
      {
         Result += Str.substr(i);
         break;
      }
      Result += Str.substr(i, j - i);
      i = j + 1;
      if ( Str[i] == '{' )
      {
         i++;
         j = Str.find('}', i);
         if ( j == HString::npos )
         {
            break;
         }
         EnvName = Str.substr(i, j - i);
         if ( EnvMap != NULL && EnvMap->find(EnvName) != EnvMap->end() )
         {
            map<HString, HString> &Map = *EnvMap;
            Env = Map[EnvName].c_str();
         }
         else
         {
            Env = getenv(EnvName.c_str());
         }
         j++;
      }
      else
      {
         for ( j = i; ; j++ )
         {
            if ( Str[j] != '_' && !isalnum(Str[j]) )
            {
               break;
            }
         }
         EnvName = Str.substr(i, j - i);
         if ( EnvMap != NULL && EnvMap->find(EnvName) != EnvMap->end() )
         {
            map<HString, HString> &Map = *EnvMap;
            Env = Map[EnvName].c_str();
         }
         else
         {
            Env = getenv(EnvName.c_str());
         }
      }
      if ( Env != NULL )
      {
         Result += Env;
      }
      i = j;
   }

   return Result;
}

bool CharInStr(char c, const char *Str)
{
   const char *s;

   for( s = Str; *s != '\0' && *s != c; s++ )
      ;

   return *s == c;
}

int ReadLine(FILE *f, char *s, int MaxLen)
{
   return ReadLine(f, s, MaxLen, true);
}

int ReadLine(FILE *f, char *s, int MaxLen, bool TabToSpace)
{
   int i;
   int c;

   for ( i = 0; i < MaxLen; )
   {
      c = fgetc(f);
      if ( c == '\0' || c == '\r' || c == 0x1a )
      {
         c = ' ';
      }
      if( TabToSpace )
      {
         if ( c == '\t' )
         {
            c = ' ';
         }
      }
      if ( c == EOF || c == '\n' )
      {
         break;
      }
      s[i++] = c;
   }

   s[i] = '\0';

   if ( i == MaxLen )
   {
      for ( ; ; )
      {
         c = fgetc(f);
         if ( c == EOF || c == '\n' )
         {
            break;
         }
      }
   }

   if ( c == EOF && i == 0 )
   {
      return -1;
   }
   else
   {
      return i;
   }
}

int ReadWord(const char *s, char *w, const char *separators, int MaxLen)
{
   int i, j;

   i = 0;

   if ( CharInStr(' ', separators) )
   {
      for ( ; s[i] == ' '; i++ )
         ;
   }

   j = 0;

   if ( s[i] == '\0' )
   {
      w[0] = '\0';
      return 0;
   }

   if ( CharInStr(s[i], separators) )
   {
      w[j++] = s[i++];
      w[j] = '\0';
      return i;
   }

   for ( ; j < MaxLen && s[i] != '\0' && !CharInStr(s[i], separators); )
   {
      w[j++] = s[i++];
   }

   w[j] = '\0';
   return i;
}

void SplitString(const HString &s, char Delimiter,
   vector<HString> &Text, bool TrimSpace)
{
   int i, j;
   int First; // 每一段的第一个非空格字符位置
   int Last = 0;  // 每一段的最后一个非空格字符位置

   Text.clear();

   if ( Delimiter == '\n' && s[0] == '\0' )
   {
      return;
   }

   for ( First = -1, j = 0, i = 0; ; )
   {
      if ( s[i] == '\0' )
      {
         if ( TrimSpace && First != -1 )
         {
            Text.push_back(s.substr(First, Last - First + 1));
         }
         else
         {
            Text.push_back(s.substr(j, i - j));
         }
         break;
      }
      if ( s[i] == Delimiter )
      {
         if ( TrimSpace && First != -1 )
         {
            Text.push_back(s.substr(First, Last - First + 1));
            First = -1;
         }
         else
         {
            Text.push_back(s.substr(j, i - j));
         }
         i++;
         j = i;
         continue;
      }
      if ( TrimSpace && s[i] != ' ' )
      {
         Last = i;
         if ( First == -1 )
         {
            First = i;
         }
      }
      i++;
   }
}

void SplitIpPort(const HString &IpPort, HString &Ip, int &Port)
{
   const char *s = IpPort.c_str();
   const char *Pos = strchr(s, ':');

   if ( Pos == NULL )
   {
      Ip = IpPort;
      Port = 0;
      return;
   }

   Ip = HString(s, Pos - s);
   Port = StrToInt(Pos + 1);
}

HString JoinStrings(const vector<HString> &Vec, char Delimiter)
{
   HString Result;

   for ( int i = 0; i < Vec.size(); i++ )
   {
      if ( i > 0 )
      {
         Result += Delimiter;
      }
      Result += Vec[i];
   }

   return Result;
}

void SplitPath(const HString &Path, HString &DirName, HString &BaseName,
   HString &Postfix)
{
   int Pos;

   Pos = Path.rfind('/');

   if ( Pos == HString::npos )
   {
      DirName = ".";
      BaseName = Path;
   }
   else
   {
      DirName = Path.substr(0, Pos);
      BaseName = Path.substr(Pos + 1);
   }

   Pos = BaseName.rfind('.');

   if( Pos == HString::npos )
   {
      Postfix = "";
   }
   else
   {
      Postfix = BaseName.substr(Pos + 1);
      BaseName = BaseName.substr(0, Pos);
   }
}

void RightTrim(HString &Str)
{
   int Pos;

   Pos = Str.find_last_not_of(' ');

   if( Pos == HString::npos )
   {
      Str.resize(0);
   }
   else
   {
      Str.resize(Pos + 1);
   }
}

void ToUpper(HString &Str)
{
   for ( int i = 0; i < Str.length(); i++ )
   {
      if ( Str[i] >= 'a' && Str[i] <= 'z' )
      {
         Str[i] -= 'a' - 'A';
      }
   }
}

void ToLower(HString &Str)
{
   for ( int i = 0; i < Str.length(); i++ )
   {
      if ( Str[i] >= 'A' && Str[i] <= 'Z' )
      {
         Str[i] += 'a' - 'A';
      }
   }
}

void ToUpper(char *s)
{
   for ( ; *s != '\0'; s++ )
   {
      if ( *s >= 'a' && *s <= 'z' )
      {
         *s -= 'a' - 'A';
      }
   }
}

void ToLower(char *s)
{
   for ( ; *s != '\0'; s++ )
   {
      if ( *s >= 'A' && *s <= 'Z' )
      {
         *s += 'a' - 'A';
      }
   }
}

static int _MatchToken(const char *Line, int &Start, const char *Tokens[])
{
   int i;
   const char *s1;
   const char *s2;

   for ( i = 0; Tokens[i] != NULL; i++ )
   {
      s1 = Line + Start;
      s2 = Tokens[i];
      for ( ; *s1 != '\0' && *s2 != '\0' && *s1 == *s2; s1++, s2++ )
         ;
      if ( *s2 == '\0' )
      {
         Start += s2 - Tokens[i];
         return i;
      }
   }

   return -1;
}

bool TextFileCompress(const HString &SrcFileName,
   const HString &DesFileName, int &ErrCode)
{
   ErrCode = 0;
   FILE *fin, *fout;

   fin = fopen(SrcFileName.c_str(), "rt");

   if ( fin == NULL )
   {
      ErrCode = errno;
      return false;
   }

   fout = fopen(DesFileName.c_str(), "wb");

   if ( fout == NULL )
   {
      ErrCode = errno;
      fclose(fin);
      return false;
   }

   if ( Lz77CompressFile(fin, fout) != 0 )
   {
      fclose(fin);
      fclose(fout);
      return false;
   }

   fclose(fin);
   fclose(fout);
   return true;
}

bool TextFileDecompress(const HString &SrcFileName,
   const HString &DesFileName, int &ErrCode)
{
   ErrCode = 0;
   FILE *fin, *fout;

   fin = fopen(SrcFileName.c_str(), "rb");

   if ( fin == NULL )
   {
      ErrCode = errno;
      return false;
   }

   fout = fopen(DesFileName.c_str(), "wb");

   if ( fout == NULL )
   {
      ErrCode = errno;
      fclose(fin);
      return false;
   }

   if ( Lz77DecompressFile(fin, fout) != 0 )
   {
      fclose(fin);
      fclose(fout);
      return false;
   }

   fclose(fin);
   fclose(fout);
   return true;
}

unsigned int Hash(const void *Buf, int Len)
{
   int i;
   const unsigned char *s = static_cast<const unsigned char *>(Buf);
   unsigned int Result;

   for( Result = 0, i = 0; i < Len; i++, s++ )
      Result = Result * 5 + *s + 1;

   return Result;
}

int StrToInt(const char*Str)
{
   int Result = 0;

   sscanf(Str, "%d", &Result);
   return Result;
}

long StrToLong(const char*Str)
{
   long Result = 0;

   sscanf(Str, "%ld", &Result);
   return Result;
}

int HexStrToInt(const char*Str)
{
   int Result = 0;

   sscanf(Str, "%x", &Result);
   return Result;
}

float StrToFloat(const char *Str)
{
   float Result = 0;

   sscanf(Str, "%f", &Result);
   return Result;
}

double StrToDouble(const char *Str)
{
   double Result = 0;

   sscanf(Str, "%lf", &Result);
   return Result;
}

long double StrToLongDouble(const char *Str)
{
   long double Result = 0;

   sscanf(Str, "%Lf", &Result);
   return Result;
}

HString IntToString(int n)
{
   char IntStr[500 + 1];

   snprintf(IntStr, 500, "%d", n);
   return HString(IntStr);
}

HString TimeToString(time_t Time)
{
   struct tm Tm;
   char TimeStr[500 + 1];

   if ( Time == 0 )
   {
      return "";
   }

   localtime_r(&Time, &Tm);
   snprintf(TimeStr, 500, "%04d%02d%02d%02d%02d%02d",
      Tm.tm_year + 1900, Tm.tm_mon + 1, Tm.tm_mday,
      Tm.tm_hour, Tm.tm_min, Tm.tm_sec);
   return HString(TimeStr);
}

time_t StringToTime(const HString &TimeStr)
{
   return StringToTime(TimeStr.c_str());
}

time_t StringToTime(const char *TimeStr)
{
   if ( TimeStr[0] == '\0' )
   {
      return 0;
   }

   struct tm Tm;
   char TmpStr[80 + 1];

   memset(&Tm, 0, sizeof(Tm));
   memcpy(TmpStr, TimeStr, 4);
   TmpStr[4] = '\0';
   Tm.tm_year = StrToInt(TmpStr) - 1900;
   memcpy(TmpStr, TimeStr + 4, 2);
   TmpStr[2] = '\0';
   Tm.tm_mon = StrToInt(TmpStr) - 1;
   memcpy(TmpStr, TimeStr + 4 + 2, 2);
   TmpStr[2] = '\0';
   Tm.tm_mday = StrToInt(TmpStr);

   if ( TimeStr[8] != '\0' )
   {
      memcpy(TmpStr, TimeStr + 8, 2);
      TmpStr[2] = '\0';
      Tm.tm_hour = StrToInt(TmpStr);
      memcpy(TmpStr, TimeStr + 8 + 2, 2);
      TmpStr[2] = '\0';
      Tm.tm_min = StrToInt(TmpStr);
      memcpy(TmpStr, TimeStr + 8 + 2 + 2, 2);
      TmpStr[2] = '\0';
      Tm.tm_sec = StrToInt(TmpStr);
   }

   Tm.tm_isdst = -1;
   return mktime(&Tm);
}

HString CurrentDateTime()
{
   return TimeToString(time(NULL));
}

void GetIpAddrVec(vector<HString> &Vec)
{
   Vec.clear();

#ifdef BK_LINUX
   struct ifaddrs *Ifaddr;

   if ( getifaddrs(&Ifaddr) == -1 )
   {
      return;
   }

   for ( struct ifaddrs *p = Ifaddr; p != NULL; p = p->ifa_next)
   {
      if ( p->ifa_addr == NULL)
      {
         continue;
      }
      int Family = p->ifa_addr->sa_family;
      if ( Family != AF_INET )
      {
         continue;
      }
      char Addr[200 + 1];
      if ( getnameinfo(p->ifa_addr, sizeof(struct sockaddr_in), Addr, 200, NULL, 0, NI_NUMERICHOST) == 0 )
      {
         Vec.push_back(Addr);
      }
    }
    freeifaddrs(Ifaddr);
#endif
}

bool CheckDate(const char *DateStr)
{
   int Len = strlen(DateStr);

   if ( Len == 0 )
   {
      return true;
   }

   if ( Len != 8 )
   {
      return false;
   }

   for ( int i = 0; i < 8; i++ )
   {
      if ( !isdigit(DateStr[i]) )
      {
         return false;
      }
   }

   char TmpStr[200 + 1];
   int Year;
   int Month;
   int Day;

   memcpy(TmpStr, DateStr, 4);
   TmpStr[4] = '\0';
   Year = StrToInt(TmpStr);
   memcpy(TmpStr, DateStr + 4 , 2);
   TmpStr[2] = '\0';
   Month = StrToInt(TmpStr);
   memcpy(TmpStr, DateStr + 6 , 2);
   TmpStr[2] = '\0';
   Day = StrToInt(TmpStr);

   if ( Year < 1900 || Year > 3000 )
   {
      return false;
   }

   if ( Month < 1 || Month > 12 )
   {
      return false;
   }

   bool LeapFlag = false;

   if ( Year % 4 == 0 )
   {
      LeapFlag = true;
      if ( Year % 100 == 0 && Year % 400 != 0 )
      {
         LeapFlag = false;
      }
   }

   static int DaysArray[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
   int Days = DaysArray[Month - 1];

   if ( LeapFlag && Month == 2 )
   {
      Days++;
   }

   if ( Day < 0 || Day > Days )
   {
      return false;
   }

   return true;
}

// class HRaw

HRaw::HRaw(int InitSize, int StepSize)
{
   if( InitSize <= 0 )
      InitSize = 1;

   FSize = 0;
   FCapacity = InitSize;
   FInitSize = InitSize;
   FStepSize = StepSize;
   FArray.resize(FCapacity);
   FArray[0] = '\0';
}

HRaw::HRaw(const HString &Str,
   int InitSize, int StepSize)
{
   if( InitSize <= Str.length() )
      InitSize = Str.length() + 1;

   FSize = Str.length();
   FCapacity = InitSize;
   FInitSize = InitSize;
   FStepSize = StepSize;
   FArray.resize(FCapacity);
   memcpy(&FArray[0], Str.c_str(), Str.length() + 1);
}

HRaw::~HRaw(void)
{
}

HRaw::HRaw(const HRaw &Obj)
{
   *this = Obj;
}

HRaw & HRaw::operator=(const HRaw &Obj)
{
   if( this == &Obj )
      return *this;

   FSize = Obj.FSize;
   FCapacity = Obj.FCapacity;
   FInitSize = Obj.FInitSize;
   FStepSize = Obj.FStepSize;
   FArray = Obj.FArray;
   return *this;
}

int HRaw::size(void) const
{
   return FSize;
}

void HRaw::resize(int Size)
{
   int i;

   if( FCapacity < Size + 1 )
   {
      i = Size + 1 - FCapacity;
      FCapacity += i + FStepSize - 1;
      FCapacity /= FStepSize;
      FCapacity *= FStepSize;
      FArray.resize(FCapacity);
   }

   FSize = Size;
   FArray[FSize] = '\0';
}

void HRaw::clear(void)
{
   FCapacity = FInitSize;
   FArray.resize(FCapacity);
   FSize = 0;
   FArray[0] = '\0';
}

void HRaw::push_back(const char *Data, int Len)
{
   int i;

   if( FCapacity < FSize + Len + 1 )
   {
      i = FSize + Len + 1 - FCapacity;
      FCapacity += i + FStepSize - 1;
      FCapacity /= FStepSize;
      FCapacity *= FStepSize;
      FArray.resize(FCapacity);
   }

   memcpy(&(FArray[0]) + FSize, Data, Len);
   FSize += Len;
   FArray[FSize] = '\0';
}

void HRaw::push_back(const HString &Str)
{
   push_back(Str.c_str(), Str.length());
}

char & HRaw::operator[](int Index)
{
   return FArray[Index];
}

const char & HRaw::operator[](int Index) const
{
   return FArray[Index];
}

char * HRaw::data(void)
{
   return &(FArray[0]);
}

const char * HRaw::data(void) const
{
   return &(FArray[0]);
}

HString HRaw::sub_string(int Offset, int Len) const
{
   return HString(&(FArray[Offset]), Len);
}

int HRaw::pos(const char *Buf, int Len) const
{
   int i;

   for( i = 0; i < Len && i + Len <= FSize; i++ )
   {
      if( memcmp(&(FArray[0]) + i, Buf, Len) == 0 )
         return i;
   }

   return -1;
}

int HRaw::pos(const HString &Str) const
{
   return pos(Str.c_str(), Str.length());
}

// class HStream
  
HStream::~HStream()
{
}

HFileStream::HFileStream(FILE *File)
   : HStream()
{
   FFile = File;
   FRecLen = -1;

   if ( FRecLen != -1 )
   {
      FLine = (char*)malloc(FRecLen);
   }
   else
   {
      FLine = NULL;
   }
}

HFileStream::~HFileStream()
{
   if ( FLine != NULL )
   {
      free(FLine);
      FLine = NULL;
   }
}

int HFileStream::ReadLine(char *Line, int MaxLen, bool TabToSpace)
{
   if ( FFile == NULL )
   {
      return -1;
   }

   if ( FRecLen == -1 )
   {
      return h_base::ReadLine(FFile, Line, MaxLen, TabToSpace);
   }

   if ( fread(FLine, FRecLen, 1, FFile) != 1 )
   {
      return -1;
   }

   int Len = FRecLen < MaxLen ? FRecLen : MaxLen;

   for ( int i = 0; i < Len; i++ )
   {
      if ( FLine[i] == '\0' )
      {
         FLine[i] = ' ';
         continue;
      }
#if !defined(BK_S390)
      if ( FLine[i] == '\r' || FLine[i] == 0x1a )
      {
         FLine[i] = ' ';
      }
#endif
      if ( TabToSpace )
      {
         if ( FLine[i] == '\t' )
         {
            FLine[i] = ' ';
         }
      }
   }

   memcpy(Line, FLine, Len);
   Line[Len] = '\0';
   return Len;
}

void HFileStream::WriteLine(const char *Line)
{
   if ( FFile == NULL )
   {
      return;
   }

   if ( FRecLen == -1 )
   {
      if ( fprintf(FFile, "%s\n", Line) < 0 )
      {
         fprintf(stderr, "fprintf fail, errno = %d\n", errno);
         exit(12);
      }
      return;
   }

   int Len = strlen(Line);

   if ( Len < FRecLen )
   {
      memcpy(FLine, Line, Len);
      memset(FLine + Len, ' ', FRecLen - Len);
   }
   else
   {
      memcpy(FLine, Line, FRecLen);
   }

   if ( fwrite(FLine, FRecLen, 1, FFile) != 1 )
   {
      fprintf(stderr, "fwrite fail, errno = %d\n", errno);
      exit(12);
   }
}

int HFileStream::ReadRec(char *Buf, int MaxLen)
{
   if ( FFile == NULL || FRecLen == -1 )
   {
      return -1;
   }

   if ( fread(FLine, FRecLen, 1, FFile) != 1 )
   {
      return -1;
   }

   if ( MaxLen > FRecLen )
   {
      memcpy(Buf, FLine, FRecLen);
      memset(Buf + FRecLen, ' ', MaxLen - FRecLen);
      return FRecLen;
   }
   else
   {
      memcpy(Buf, FLine, MaxLen);
      return MaxLen;
   }
}

int HFileStream::WriteRec(const char *Rec, int Len)
{
   if ( FRecLen == -1 )
   {
      if ( fwrite(Rec, Len, 1, FFile) != 1 )
      {
         return -1;
      }
      else
      {
         return 0;
      }
   }

   if ( Len < FRecLen )
   {
      memcpy(FLine, Rec, Len);
      memset(FLine + Len, ' ', FRecLen - Len);
   }
   else
   {
      memcpy(FLine, Rec, FRecLen);
   }

   fwrite(FLine, FRecLen, 1, FFile);
   return 0;
}

HMemStream::HMemStream(void *Buf, int Len)
   : HStream(), FPos(0)
{
   if ( Len == 0 )
   {
      return;
   }

   FMem.resize(Len);
   memcpy(&FMem[0], Buf, Len);
}

int HMemStream::ReadLine(char *Line, int MaxLen, bool TabToSpace)
{
   if ( FPos == FMem.size() )
   {
      return -1;
   }

   int i;

   for ( i = 0; FPos < FMem.size() && i < MaxLen &&
                FMem[FPos] != '\n'; FPos++, i++ )
   {
      if ( TabToSpace && FMem[FPos] == '\t' )
      {
         Line[i] = ' ';
      }
      else
      {
         Line[i] = FMem[FPos];
      }
   }

   if ( i == MaxLen )
   {
      for ( ; FPos < FMem.size() && FMem[FPos] != '\n'; FPos++ )
         ;
      if ( FPos < FMem.size() )
      {
         FPos++;
      }
   }
   else
   {
      if ( FPos < FMem.size() )
      {
         FPos++;
      }
   }

   Line[i] = '\0';
   return i;
}

void HMemStream::WriteLine(const char *Line)
{
   int CurrLen = FMem.size();
   int Len = strlen(Line);

   FMem.resize(CurrLen + Len + 1);
   memcpy(&FMem[0] + CurrLen, Line, Len);
   FMem[CurrLen + Len] = '\n';
}

char *HMemStream::GetBuf(void)
{
   return &FMem[0];
}

int HMemStream::GetLength(void)
{
   return FMem.size();
}

int HVecStream::ReadLine(char *Line, int MaxLen, bool TabToSpace)
{
   if ( Idx == StrVec.size() )
   {
      return -1;
   }

   if ( StrVec[Idx].length() > MaxLen )
   {
      memcpy(Line, StrVec[Idx].c_str(), MaxLen);
      Line[MaxLen] = '\0';
      return MaxLen;
   }

   BkStrNCpy(Line, StrVec[Idx].c_str(), MaxLen);
   return StrVec[Idx++].length();
}

void HVecStream::WriteLine(const char *Line)
{
   StrVec.push_back(Line);
}

}; // namespace h_base
